package org.korosoft.findbugs.aggregator;

import org.apache.commons.cli.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;

import java.io.File;
import java.net.URL;

public final class App {
    private static final Log LOG = LogFactory.getLog(App.class);
    static final Options OPTIONS = new Options();

    static {
        OPTIONS.addOption("s", "source-dir", true, "Source code root directory. FindBugs reports will be searched recursively.");
        OPTIONS.addOption("d", "dest-dir", true, "Destination directory for report files. Directory will be created if it does not exist");
        OPTIONS.addOption("f", "force", false, "Force reporting to unclean destination directory. Directory contents will be removed.");
        OPTIONS.addOption("v", "verbose", false, "Verbose output");
        OPTIONS.addOption("q", "quiet", false, "Do not output");
        OPTIONS.addOption("x", "exceptions", false, "Show exception stack traces");
        OPTIONS.addOption("h", "help", false, "Show usage");
    }

    public static void usage() {
        final HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("findbugs-aggregator -s <SOURCE_DIR> -d <DEST_DIR>", OPTIONS);
    }

    public static void main(String[] args) throws Exception {
        final CommandLineParser cmdParser = new PosixParser();
        final CommandLine cmd;
        try {
            cmd = cmdParser.parse(OPTIONS, args, true);
        } catch (ParseException e) {
            usage();
            System.exit(1);
            return;
        }
        if (cmd.hasOption('h' ) || !cmd.hasOption('s' ) || !cmd.hasOption('d' ) || (cmd.hasOption('q' ) && (cmd.hasOption('v' )))) {
            usage();
            System.exit(1);
            return;
        }

        // Process logging options
        Level level = cmd.hasOption('q' ) ? Level.OFF : (cmd.hasOption('v' ) ? Level.DEBUG : Level.INFO);
        LogManager.getLogger("org.korosoft").setLevel(level);

        // Set exception handler
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if (cmd.hasOption('x' )) {
                    LOG.error("Error: " + e);
                } else {
                    LOG.error("Error: " + e.getMessage());
                }
                System.exit(1);
            }
        });

        LOG.debug("Starting");

        // Check for templates

        String templateDirName = System.getProperty("findbugs.aggregator.templates");
        if (templateDirName == null) {
            URL resource = App.class.getResource("/dynamic/index.ftl");
            if (resource != null) {
                File resourceFile = new File(resource.getFile());
                if (resourceFile.exists()) {
                    templateDirName = resourceFile.getParent();
                    if (templateDirName != null) {
                        templateDirName = new File(templateDirName).getParent();
                    }
                }
            }
        }
        final File templateDir = templateDirName != null ? new File(templateDirName) : null;
        if (templateDir == null || !templateDir.exists()) {
            throw new AggregatorException("Templates dir not found");
        }

        // Prepare output directory
        final File outputDir = new File(cmd.getOptionValue('d' ));

        if (outputDir.exists()) {
            if (outputDir.listFiles().length > 0) {
                if (cmd.hasOption('f' )) {
                    LOG.info("Removing directory " + outputDir.getAbsolutePath());
                    FileUtils.deleteDirectory(outputDir);
                } else {
                    throw new AggregatorException("Target directory is not empty");
                }
            }
        }
        if (!outputDir.exists() && !outputDir.mkdirs()) {
            throw new AggregatorException("Failed to create directory " + outputDir.getAbsolutePath());
        }
        LOG.debug("Searching for findbugs.xml files...");
        final Crawler crawler = new Crawler();
        final BugCollector collector = new BugCollector();
        final FindBugsParser parser = new FindBugsParser();
        final Formatter formatter = new Formatter();

        crawler.doCrawl(new File(cmd.getOptionValue('s' )), new Crawler.Callback() {
            @Override
            public void process(File foundFile) {
                parser.parse(foundFile, new FindBugsParser.Callback() {
                    @Override
                    public void process(Bug bug) {
                        collector.addBug(bug);
                    }
                });
            }
        }

        );
        LOG.info("Found " + collector.getCount() + " bugs");

        formatter.formatPages(outputDir, templateDir, collector);
    }

    private App() {
    }
}
